#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package}.${artifactId}.annotation.aspect;


import com.google.common.collect.Maps;
import com.google.common.util.concurrent.RateLimiter;
import ${package}.${artifactId}.annotation.Limit;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import tk.mybatis.mapper.util.Assert;

import java.lang.reflect.Method;
import java.util.Map;

/**
 * @Author: shenyang
 * @CreateTime: 2023-12-01  10:29
 * @Description: TODO
 * @Version: 1.0
 */


@Aspect
@Component
public class LimitAspect {

    private static final Logger logger = LoggerFactory.getLogger(LimitAspect.class);

    private final Map<String, RateLimiter> limitMap = Maps.newConcurrentMap();

    @Before(value = "@annotation(${package}.${artifactId}.annotation.Limit)")
    public void around(JoinPoint pjp) {
        MethodSignature signature = (MethodSignature) pjp.getSignature();
        Method method = signature.getMethod();
        //拿limit的注解
        Limit limit = method.getAnnotation(Limit.class);
        if (limit == null) {
            return;
        }
        //key作用：不同的接口，不同的流量控制
        String key = limit.key();
        RateLimiter rateLimiter;
        //验证缓存是否有命中key
        extracted(limit, key);
        rateLimiter = limitMap.get(key);
        // 拿令牌
        boolean acquire = rateLimiter.tryAcquire(limit.timeout(), limit.timeunit());
        // 拿不到命令，直接返回异常提示
        Assert.isTrue(acquire, limit.msg());

    }

    private synchronized void extracted(Limit limit, String key) {
        RateLimiter rateLimiter;
        if (!limitMap.containsKey(key)) {
            // 创建令牌桶（每秒生成令牌数）
            rateLimiter = RateLimiter.create(limit.permitsPerSecond());
            limitMap.put(key, rateLimiter);
            logger.info("新建了令牌桶={}，容量={}", key, limit.permitsPerSecond());
        }
    }

}